#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <proto.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <server_conf.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <media_lib.h>
#include <thr_channel.h>
#include <thr_list.h>

struct server_conf conf = {
    .rcvport = DEFAULT_RCVPORT,
    .mgroup = DEFAULT_MGROUP,
    .mediaDir = DEFAULT_MEDIA_DIR,
    .ifName = DEFAULT_IF,
    .runMode = RUN_FORGROUND,
};
int serverSd = -1;
struct sockaddr_in sndAddr;
socklen_t sndAddrLen = sizeof(sndAddr);
static struct mlib_listentry_st* list = NULL;  // 频道概览信息指针
static int list_size = 0;  // 频道个数

static void print_help(void) {
    printf("-M  指定多播组\n");
    printf("-P  指定接收端口\n");
    printf("-F  前台运行\n");
    printf("-I  选择网卡\n");
    printf("-D  媒体文件位置\n");
    printf("-H  打印帮助\n");
    fflush(stdout);
}

static void daemon_exit(int signalNO) {
    if (signalNO == SIGTERM || signalNO == SIGINT || signalNO ==SIGQUIT) {
        syslog(LOG_WARNING, "daemon_exit\n");

        if (serverSd > 0) {
            close(serverSd);
            serverSd = -1;
        }

        closelog();

        thr_list_destroy(); // 销毁发送节目单的线程

        if (list != NULL) {
            mlib_freechnlist(list);
			list = NULL;
        }

        thr_channel_destroyAll();

        exit(1);
    } 
}

// 守护进程化
static void daemonize(void) {
    pid_t pid;
    pid = fork();
    if (pid < 0) {
        syslog(LOG_ERR, "fork error, %s\n", strerror(errno));
        exit(1);
    }
    else if (pid > 0) { // 当前进程退出，导致子进程成为孤儿进程
        exit(0);
    }

    // 子进程
    int fd = open("/dev/null", O_RDWR);
    if (fd < 0) {
        syslog(LOG_ERR, "open error, %s\n", strerror(errno));
        exit(1);
    }
    dup2(fd, STDIN_FILENO);
    dup2(fd, STDOUT_FILENO);
    dup2(fd, STDERR_FILENO);

    pid = setsid();

    chdir("/");
    umask(0);
}

static void init_signal(void) {
    struct sigaction sa;
    sa.sa_handler = daemon_exit;
    sigemptyset(&sa.sa_mask);
    sigaddset(&sa.sa_mask, SIGTERM);
    sigaddset(&sa.sa_mask, SIGINT);
    sigaddset(&sa.sa_mask, SIGQUIT);
    sigaction(SIGTERM, &sa, NULL);
    sigaction(SIGINT, &sa, NULL);
    sigaction(SIGQUIT, &sa, NULL);
}

static int socket_init(void) {
    int sd = -1;
    int ret = -1;
    sd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sd < 0) {
        syslog(LOG_ERR, "socket error,%s\n", strerror(errno));
        closelog();
        exit(1);
    }

    struct ip_mreqn mreq;
    inet_pton(AF_INET, conf.mgroup, &mreq.imr_multiaddr);   // 设置多播地址
    inet_pton(AF_INET, "0.0.0.0", &mreq.imr_address);   // 设置本地地址
    mreq.imr_ifindex = if_nametoindex(conf.ifName);
    // 设置ip层属性
    if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq)) < 0) { // 创建多播组
        syslog(LOG_ERR, "setsockopt error,%s\n", strerror(errno));
        closelog();
        close(sd);
        exit(1);
    }

    // bind

    // 发送端ip和端口
    sndAddr.sin_family = AF_INET;
    ret = inet_pton(AF_INET, conf.mgroup, &sndAddr.sin_addr.s_addr);
    sndAddr.sin_port = htons(atoi(conf.rcvport));

    return sd;
}

/*
    -M  指定多播组
    -P  指定接收端口
    -F  前台运行
    -I  选择网卡
    -D  媒体文件位置
    -H  打印帮助
*/
int main(int argc, char** argv) {
    // 命令行分析
    while (1) {
        int c = getopt(argc, argv, "M:P:FD:I:H:");
        if (c < 0) {
            break;
        }
        switch (c) {
            case 'M':
                conf.mgroup = optarg;
                break;
            case 'P':
                conf.rcvport = optarg;
                break;
            case 'F':
                conf.runMode = RUN_FORGROUND;
                break;
            case 'D':
                conf.mediaDir = optarg;
                break;
            case 'I':
                conf.ifName = optarg;
                break;
            case 'H':
                print_help();
                exit(1);
                break;
            default:
                abort();
                break;
        }
    }

    // 接入系统日志
    openlog("StreamBroadcast", LOG_PID|LOG_PERROR, LOG_DAEMON);

    // 信号操作
    init_signal();
    syslog(LOG_INFO, "init_signal successed\n");
    
    // 实现为守护进程
    if (conf.runMode == RUN_DAEMON) {
        syslog(LOG_WARNING, "run background\n");
        daemonize();
    }
    else if (conf.runMode == RUN_FORGROUND) {
        syslog(LOG_WARNING, "run forground\n");
    }
    else {
        syslog(LOG_ERR, "run mode error\n");
        exit(1);
    }

    // socket初始化
    serverSd = socket_init();
    syslog(LOG_INFO, "socket_init successed,serverSd:%d\n", serverSd);

    // 获取频道信息\频道概览
    int ret = -1;
    ret = mlib_getchnlist(&list, &list_size);
    if (ret != 0) {
        syslog(LOG_ERR, "mlib_getchnlist error\n");
        exit(1);
    }
    syslog(LOG_INFO, "mlib_getchnlist successed,list:%p,list_size:%d\n", list, list_size);

    // 创建节目单线程
    ret = thr_list_create(list, list_size);
    if (ret != 0) {
        syslog(LOG_ERR, "thr_list_create error");
        mlib_freechnlist(list);
        exit(1);
    }
    syslog(LOG_INFO, "thr_list_create successed\n");

    // 创建频道线程
    int i = 0;
    for (i = 0; i < list_size; i++) {
        ret = thr_channel_create(list+i);
    }
    syslog(LOG_INFO, "%d channels created\n", i);

    while (1) {
        pause();
    }

    closelog();

    exit(0);
}
